# -*- coding:utf8 -*-
from math import pi
import sys
from random import random, choice
from copy import copy
import numpy as np
from collections import Counter
from matplotlib.pylab import *
 
try:
    import psyco
    psyco.full()
except ImportError:
    pass

FLOAT_MAX = 1e100

class Point:
    __slots__ = ["x", "y", "z", "group","user_id"]
    def __init__(self, x=0.0, y=0.0,z=0.0, group=0,user_id = "ID"):
        self.x, self.y, self.z, self.group, self.user_id = x, y, z, group, user_id

def generate_points_from_txt(radius,src_file):
    forward_comment_like = []
    user_id = []
    for line in open(src_file,'r'):
        list_line = line.split()
        forward_comment_like.append(list_line[3:6])
        user_id.append(list_line[0])
    forward_comment_like = np.asarray(forward_comment_like,dtype='float32')
    points = [Point() for _ in xrange(len(forward_comment_like))]
    print "generatting points"
    index = 0
    for p in points:
        r = random() * radius
        ang = random() * 2 * pi
        p.x = forward_comment_like[index][0]
        p.y = forward_comment_like[index][1]
        p.z = forward_comment_like[index][2]
        p.user_id = user_id[index]
        index += 1
    return points

def nearest_cluster_center(point, cluster_centers):
    """
    每个点都可以通过这个函数获得离其最近的centers的坐标及距离
    """
    def sqr_distance_3D(a, b):
        return (a.x - b.x) ** 2  +  (a.y - b.y) ** 2  +  (a.z - b.z) ** 2
 
    min_index = point.group
    min_dist = FLOAT_MAX
 
    for i, cc in enumerate(cluster_centers):
        d = sqr_distance_3D(cc, point)
        if min_dist > d:
            min_dist = d
            min_index = i
 
    return (min_index, min_dist)
 
 
def kpp(points, cluster_centers):
    """
    k-means++ 初始化
    """
    print "initializing seeds"
    cluster_centers[0] = copy(choice(points))
    d = [0.0 for _ in xrange(len(points))]

    for i in xrange(1, len(cluster_centers)):
        say = "\r\tnum.cluster center : " + str(i)
        sys.stdout.write(say)
        sys.stdout.flush()
        sum = 0
        for j, p in enumerate(points):
            d[j] = nearest_cluster_center(p, cluster_centers[:i])[1]
            sum += d[j]
 
        sum *= random()
 
        for j, di in enumerate(d):
            sum -= di
            if sum > 0:
                continue
            cluster_centers[i] = copy(points[j])
            break
    for p in points:
        p.group = nearest_cluster_center(p, cluster_centers)[0]
 
 
def lloyd(points, nclusters):
    cluster_centers = [Point() for _ in xrange(nclusters)]

    kpp(points, cluster_centers)

    lenpts10 = len(points) >> 10

    iteraction = 1
    print "\nrunning ..."
    while True:
        # group element for centroids are used as counters
        say = "\r\titeraction : " + str(iteraction)
        sys.stdout.write(say)
        sys.stdout.flush()
        for cc in cluster_centers:
            cc.x = 0
            cc.y = 0
            cc.z = 0
            cc.group = 0
 
        for p in points:
            cluster_centers[p.group].group += 1 #计算属于该group的有多少个点
            cluster_centers[p.group].x += p.x
            cluster_centers[p.group].y += p.y
            cluster_centers[p.group].z += p.z
 
        for cc in cluster_centers:
            cc.x /= cc.group
            cc.y /= cc.group
            cc.z /= cc.group

        changed = 0
        for p in points:
            min_i = nearest_cluster_center(p, cluster_centers)[0]
            if min_i != p.group:
                changed += 1
                p.group = min_i

        if changed <= lenpts10:
            break
        iteraction += 1
    for i, cc in enumerate(cluster_centers):
        cc.group = i
 
    return cluster_centers

def save_clusters_to_txt(cluster_centers):
    print "\nsaving cluster_centers to txt"
    f = open("C:\\Users\\wuxiaomin\\Desktop\\data\\SinaWeiBo\\offline\\clusters.txt","w")
    for j in range(len(cluster_centers)):
            f.write(str(cluster_centers[j].x) + ' '+ str(cluster_centers[j].y) + ' ' + str(cluster_centers[j].z) + '\n')

def judge_point_belong_to_which_group(points):
    print "judge point belong to which group..."
    userGroup = {}
    real_userGroup = {}
    flag = 1
    flag_ = 1
    for p in points:
        say = "\r\tnum.point :" + " " + str(flag)
        sys.stdout.write(say)
        sys.stdout.flush()
        if p.user_id not in userGroup:
            userGroup[p.user_id] = [p.group]
        else:
            userGroup[p.user_id].append(p.group)
        flag += 1
    print "\nreal_userGroup"
    for p in points:
        say = "\r\tnum.point :" + " " + str(flag_)
        sys.stdout.write(say)
        sys.stdout.flush()
        tmp = Counter(userGroup[p.user_id]).most_common(1)
        if p.user_id not in real_userGroup:
            real_userGroup[p.user_id] = [tmp[0][0]]
        else:
            real_userGroup[p.user_id].append(tmp[0][0])
        flag_ += 1
    return real_userGroup


def offline_test(points_test,cluster_centers,real_userGroup):
    print "\noffline testing ..."
    precisions = []
    for p in points_test:
        predict = cluster_centers[real_userGroup[p.user_id][0]]
        Ef = abs(p.x - int(cluster_centers[predict][0]))/float(int(cluster_centers[predict][0]) + 5)
        Ec = abs(p.x - int(cluster_centers[predict][1]))/float(int(cluster_centers[predict][1]) + 3)
        El = abs(p.x - int(cluster_centers[predict][2]))/float(int(cluster_centers[predict][2]) + 3)
        ci = int(cluster_centers[predict][0]) + int(cluster_centers[predict][1]) +int(cluster_centers[predict][2])
        if ci > 100:
            ci = 100
        pi = 1 - 0.5 * Ef - 0.25 * Ec - 0.25 * El
        if pi > 0.8:
            pi = 1
        else:
            pi = 0
        up = up + (ci + 1)*pi
        down = down +  (ci + 1)
    acc = up/float(down)
    return acc


def generate_accu(k):
    points = generate_points_from_txt(10,"C:\\Users\\wuxiaomin\\Desktop\\data\\SinaWeiBo\\offline\\offline_train.txt")
    cluster_centers = lloyd(points, k)
    save_clusters_to_txt(cluster_centers)
    judge_point_belong_to_which_group(points)
    points_test =generate_points_from_txt(10,"C:\\Users\\wuxiaomin\\Desktop\\data\\SinaWeiBo\\offline\\offline_test.txt")
    real_userGroup = judge_point_belong_to_which_group(points)
    acuuracy = offline_test(points_test,cluster_centers,real_userGroup)
    print "\naccuracy : " + str(acuuracy)

if __name__ == "__main__":
    accus = []
    for i in xrange(10):
        accu = generate_accu(k=10*(i+1))
        accus.append(accu)
    plot(accus)
    plt.savefig('C:\\Users\\wuxiaomin\\Desktop\\data\\SinaWeiBo\\offline\\accus.png', dpi=64)
    show()
    f = open("C:\\Users\\wuxiaomin\\Desktop\\data\\SinaWeiBo\\offline\\accus.txt","w")
    for i in range(len(accus)):
        f.write(accus[i]+"\n")


